import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import { Logger } from '../../../src/logger/Logger';

/**
 * GN文件管理器
 * 负责解析和修改BUILD.gn文件，将新生成的测试文件添加到构建系统中
 */
export class GNFileManager {
    
    /**
     * 查找项目根目录（包含 bundle.json、repo.yaml 或多个模块目录的目录）
     * @param startPath 起始路径
     * @returns 项目根目录，如果找不到则返回null
     */
    public static findProjectRoot(startPath: string): string | null {
        let currentPath = path.dirname(startPath);
        const root = path.parse(currentPath).root;
        
        let depth = 0;
        let fallbackRoot: string | null = null;  // 备用根目录
        
        while (currentPath !== root && depth < 15) {
            // 检查是否是项目根目录的标志
            const hasBundleJson = fs.existsSync(path.join(currentPath, 'bundle.json'));
            const hasRepoYaml = fs.existsSync(path.join(currentPath, 'repo.yaml'));
            const hasBuildGn = fs.existsSync(path.join(currentPath, 'BUILD.gn'));
            
            // 检查是否有典型的 OpenHarmony 目录
            const hasTest = fs.existsSync(path.join(currentPath, 'test')) && 
                           fs.statSync(path.join(currentPath, 'test')).isDirectory();
            const hasServices = fs.existsSync(path.join(currentPath, 'services')) && 
                               fs.statSync(path.join(currentPath, 'services')).isDirectory();
            const hasInterfaces = fs.existsSync(path.join(currentPath, 'interfaces')) && 
                                 fs.statSync(path.join(currentPath, 'interfaces')).isDirectory();
            
            // 优先级1：有 bundle.json 或 repo.yaml，且有 test 目录（最可靠）
            if ((hasBundleJson || hasRepoYaml) && hasTest) {
                Logger.info(`[GNFileManager] 找到项目根目录（优先级1）: ${currentPath}`);
                return currentPath;
            }
            
            // 优先级2：有 bundle.json 且有 services 或 interfaces（作为备用）
            if ((hasBundleJson || hasRepoYaml) && (hasServices || hasInterfaces)) {
                if (!fallbackRoot) {
                    fallbackRoot = currentPath;
                    Logger.info(`[GNFileManager] 找到备用项目根目录: ${currentPath}`);
                }
            }
            
            // 优先级3：有 BUILD.gn 且同时有 test 和 services
            if (hasBuildGn && hasTest && hasServices) {
                Logger.info(`[GNFileManager] 找到项目根目录（优先级3）: ${currentPath}`);
                return currentPath;
            }
            
            currentPath = path.dirname(currentPath);
            depth++;
        }
        
        // 如果没找到最佳匹配，返回备用根目录
        if (fallbackRoot) {
            Logger.info(`[GNFileManager] 使用备用项目根目录: ${fallbackRoot}`);
            return fallbackRoot;
        }
        
        return null;
    }
    
    /**
     * 查找测试目录的 BUILD.gn 文件
     * @param projectRoot 项目根目录
     * @returns 测试 BUILD.gn 文件路径，如果找不到则返回null
     */
    private static findTestBuildGn(projectRoot: string): string | null {
        // 优先查找路径
        const testPaths = [
            'test/unittest/BUILD.gn',
            'test/BUILD.gn',
            'tests/unittest/BUILD.gn',
            'tests/BUILD.gn'
        ];
        
        for (const testPath of testPaths) {
            const fullPath = path.join(projectRoot, testPath);
            if (fs.existsSync(fullPath)) {
                Logger.info(`[GNFileManager] 找到测试 BUILD.gn: ${fullPath}`);
                return fullPath;
            }
        }
        
        return null;
    }
    
    /**
     * 查找指定目录及其父目录中的BUILD.gn文件
     * 智能策略：
     * 1. 首先向上查找最近的 BUILD.gn
     * 2. 检查该 BUILD.gn 是否包含测试目标
     * 3. 如果不包含测试目标，尝试查找项目的 test/unittest/BUILD.gn
     * 4. 返回包含测试目标的 BUILD.gn，优先返回测试目录的
     * 
     * @param startPath 起始路径
     * @returns BUILD.gn文件的完整路径，如果找不到则返回null
     */
    public static findBuildGnFile(startPath: string): string | null {
        if (!startPath) {
            Logger.warn('findBuildGnFile: startPath is undefined or empty');
            return null;
        }
        
        let currentPath = path.dirname(startPath);
        const root = path.parse(currentPath).root;
        
        // 第一步：向上查找最近的 BUILD.gn
        let nearestBuildGn: string | null = null;
        let tempPath = currentPath;
        let depth = 0;
        
        while (tempPath !== root && depth < 10) {
            const buildGnPath = path.join(tempPath, 'BUILD.gn');
            if (fs.existsSync(buildGnPath)) {
                nearestBuildGn = buildGnPath;
                Logger.info(`[GNFileManager] 找到最近的 BUILD.gn: ${buildGnPath}`);
                break;
            }
            tempPath = path.dirname(tempPath);
            depth++;
        }
        
        // 第二步：检查是否包含测试目标
        if (nearestBuildGn) {
            const targets = this.parseTestTargets(nearestBuildGn);
            if (targets.length > 0) {
                Logger.info(`[GNFileManager] BUILD.gn 包含 ${targets.length} 个测试目标，直接使用`);
                return nearestBuildGn;
            }
            Logger.info(`[GNFileManager] BUILD.gn 不包含测试目标，尝试查找测试目录的 BUILD.gn`);
        }
        
        // 第三步：查找项目根目录
        const projectRoot = this.findProjectRoot(startPath);
        if (!projectRoot) {
            Logger.warn(`[GNFileManager] 未找到项目根目录，返回最近的 BUILD.gn: ${nearestBuildGn}`);
            return nearestBuildGn;
        }
        
        // 第四步：查找测试目录的 BUILD.gn
        const testBuildGn = this.findTestBuildGn(projectRoot);
        if (testBuildGn) {
            const targets = this.parseTestTargets(testBuildGn);
            if (targets.length > 0) {
                Logger.info(`[GNFileManager] 找到测试 BUILD.gn (${targets.length} 个目标)，优先使用: ${testBuildGn}`);
                return testBuildGn;
            }
        }
        
        // 第五步：如果测试 BUILD.gn 也没有测试目标，返回最近的那个
        Logger.warn(`[GNFileManager] 测试 BUILD.gn 也无测试目标或不存在，返回最近的 BUILD.gn: ${nearestBuildGn}`);
        return nearestBuildGn;
    }

    /**
     * 增强版：为给定源文件查找可能的 BUILD.gn 候选（多处拥有者场景）
     * 策略：
     *  - 向上最多 5 层，收集每层的同级与子目录（1 层深度）中的 BUILD.gn
     *  - 在候选 BUILD.gn 中进行“宽松匹配”：
     *     1) sources 中显式包含 basename
     *     2) 存在包含 "/src/" 且以 .cpp/.cc 结尾的条目，且条目字符串中含有 basename（即使前缀是变量如 ${var}/src/...）
     *     3) 行内包含 rebase_path/join 路径且含有 basename
     *  - 返回按启发式排序：显式命中 > 变量命中 > 目录聚合命中；同分按路径长度升序
     */
    public static findCandidateBuildGnsForSource(startPath: string, sourceFilePath: string): string[] {
        try {
            const sourceBasename = path.basename(sourceFilePath);
            const startDir = path.dirname(startPath);
            const root = path.parse(startDir).root;
            const visited = new Set<string>();
            const candidates: Array<{ gn: string; score: number; pathLen: number }> = [];

            let cur = startDir;
            let up = 0;
            while (cur !== root && up < 5) {
                // 当前目录及一层子目录中的 BUILD.gn
                const dirsToScan: string[] = [cur];
                try {
                    const sub = fs.readdirSync(cur).map(f => path.join(cur, f)).filter(p => {
                        try { return fs.statSync(p).isDirectory(); } catch { return false; }
                    });
                    // 仅一层子目录，避免扫描过深
                    dirsToScan.push(...sub);
                } catch {}

                for (const d of dirsToScan) {
                    const gn = path.join(d, 'BUILD.gn');
                    if (visited.has(gn)) continue;
                    visited.add(gn);
                    if (!fs.existsSync(gn)) continue;

                    let score = 0;
                    try {
                        const content = fs.readFileSync(gn, 'utf-8');
                        const lc = content.toLowerCase();
                        const baseLc = sourceBasename.toLowerCase();
                        const baseStem = baseLc.replace(/\.[^.]+$/, '');
                        // 显式包含 basename
                        if (lc.includes(baseLc)) {
                            score = 100;
                        } else if (lc.includes('/src/') && lc.includes(baseStem)) {
                            // 粗匹配：src 目录 + 文件主名（兼容 ${var}/src/... 变量路径）
                            score = 70;
                        } else if (/sources\s*\+?=\s*\[/.test(content)) {
                            // 存在 sources 聚合，给一点分
                            score = 40;
                        }
                    } catch {}

                    if (score > 0) {
                        candidates.push({ gn, score, pathLen: gn.length });
                    }
                }

                cur = path.dirname(cur);
                up++;
            }

            candidates.sort((a, b) => {
                if (b.score !== a.score) return b.score - a.score;
                return a.pathLen - b.pathLen;
            });

            return candidates.map(c => c.gn);
        } catch (e) {
            Logger.warn(`[GNFileManager] findCandidateBuildGnsForSource 失败: ${e}`);
            return [];
        }
    }
    
    /**
     * 解析BUILD.gn文件中的测试目标
     * @param buildGnPath BUILD.gn文件路径
     * @returns 测试目标的名称和位置信息
     */
    public static parseTestTargets(buildGnPath: string): {
        name: string;
        type: string;
        startLine: number;
        endLine: number;
        sourcesLine: number;
    }[] {
        if (!fs.existsSync(buildGnPath)) {
            return [];
        }
        
        const content = fs.readFileSync(buildGnPath, 'utf-8');
        const lines = content.split('\n');
        const targets: {
            name: string;
            type: string;
            startLine: number;
            endLine: number;
            sourcesLine: number;
        }[] = [];
        
        // 改进的匹配模式：支持更多格式
        // 匹配：ohos_unittest("name") 或 executable("name") 等，可能有或没有空格，可能跨行
        const testTargetPattern = /^\s*(ohos_unittest|executable|ohos_executable|ohos_moduletest|test)\s*\(\s*"([^"]+)"/;
        
        let currentTarget: any = null;
        let braceDepth = 0;
        let pendingTarget: any = null; // 用于处理跨行的目标定义
        
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            
            // 如果有待处理的目标，检查是否找到左大括号
            if (pendingTarget && line.includes('{')) {
                currentTarget = pendingTarget;
                braceDepth = (line.match(/\{/g) || []).length;
                pendingTarget = null;
                continue;
            }
            
            // 检查是否是测试目标的开始
            const match = line.match(testTargetPattern);
            if (match && !currentTarget) {
                const [, type, name] = match;
                Logger.info(`[GNFileManager] 找到潜在测试目标: type=${type}, name=${name}`);
                
                // 更宽松的判断：只要是测试相关的类型或名称包含test
                const isTestTarget = 
                    type === 'ohos_unittest' || 
                    type === 'ohos_moduletest' ||
                    type === 'test' ||
                    name.toLowerCase().includes('test') || 
                    name.toLowerCase().includes('unittest');
                
                if (isTestTarget) {
                    const target = {
                        type,
                        name,
                        startLine: i,
                        endLine: -1,
                        sourcesLine: -1
                    };
                    
                    // 检查同一行是否有左大括号
                    if (line.includes('{')) {
                        currentTarget = target;
                        braceDepth = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
                    } else {
                        // 目标定义可能跨行，暂存起来
                        pendingTarget = target;
                    }
                    Logger.info(`[GNFileManager] 接受测试目标: ${name}`);
                }
            } else if (currentTarget) {
                // 在当前目标内，追踪大括号深度
                const openBraces = (line.match(/\{/g) || []).length;
                const closeBraces = (line.match(/\}/g) || []).length;
                braceDepth += openBraces - closeBraces;
                
                // 查找sources行 - 支持更多格式
                const trimmedLine = line.trim();
                if (trimmedLine.startsWith('sources') && (trimmedLine.includes('[') || trimmedLine.includes('='))) {
                    currentTarget.sourcesLine = i;
                    Logger.info(`[GNFileManager] 找到 sources 行: line ${i}`);
                }
                
                // 如果大括号闭合，说明目标定义结束
                if (braceDepth === 0) {
                    currentTarget.endLine = i;
                    targets.push(currentTarget);
                    Logger.info(`[GNFileManager] 测试目标解析完成: ${currentTarget.name}, lines ${currentTarget.startLine}-${currentTarget.endLine}`);
                    currentTarget = null;
                }
            }
        }
        
        Logger.info(`[GNFileManager] 总共找到 ${targets.length} 个测试目标`);
        return targets;
    }
    
    /**
     * 在BUILD.gn文件中添加测试文件
     * @param buildGnPath BUILD.gn文件路径
     * @param testFilePath 测试文件的相对路径（相对于BUILD.gn所在目录）
     * @returns 修改结果，包含旧内容、新内容和diff信息
     */
    public static addTestFile(
        buildGnPath: string,
        testFilePath: string
    ): {
        success: boolean;
        oldContent: string;          // 预览用：修改前的上下文
        newContent: string;          // 完整文件内容：用于写入文件
        previewNewContent: string;   // 预览用：修改后的上下文
        diff: string;
        message?: string;
    } {
        if (!fs.existsSync(buildGnPath)) {
            return {
                success: false,
                oldContent: '',
                newContent: '',
                previewNewContent: '',
                diff: '',
                message: `BUILD.gn文件不存在: ${buildGnPath}`
            };
        }
        
        const content = fs.readFileSync(buildGnPath, 'utf-8');
        const targets = this.parseTestTargets(buildGnPath);
        
        if (targets.length === 0) {
            // 提供更详细的诊断信息
            const lines = content.split('\n');
            const preview = lines.slice(0, Math.min(20, lines.length)).join('\n');
            Logger.warn(`[GNFileManager] BUILD.gn 文件预览（前20行）:\n${preview}`);
            Logger.warn(`[GNFileManager] 无法在 BUILD.gn 中找到测试目标。支持的格式包括：`);
            Logger.warn(`  - ohos_unittest("test_name") {`);
            Logger.warn(`  - ohos_moduletest("test_name") {`);
            Logger.warn(`  - executable("test_name") {`);
            Logger.warn(`  - 或任何名称包含 "test" 的目标`);
            
            return {
                success: false,
                oldContent: content,
                newContent: content,
                previewNewContent: content,
                diff: '',
                message: `未在BUILD.gn中找到测试目标。
                
BUILD.gn文件位置: ${buildGnPath}

建议：
1. 检查 BUILD.gn 是否包含测试目标（如 ohos_unittest）
2. 或者选择"跳过 GN 配置"使用默认测试目录
3. 查看输出面板（HarmonyPilot）了解详细信息

如需手动添加，示例格式：
ohos_unittest("my_test") {
  sources = [
    "test/my_test.cpp",
  ]
}`
            };
        }
        
        const lines = content.split('\n');
        
        // 计算相对路径
        const buildGnDir = path.dirname(buildGnPath);
        const absoluteTestPath = path.isAbsolute(testFilePath) 
            ? testFilePath 
            : path.join(buildGnDir, testFilePath);
        const relativeTestPath = path.relative(buildGnDir, absoluteTestPath).replace(/\\/g, '/');
        
        // 提取测试文件名（不含路径）
        const testFileName = path.basename(testFilePath);
        
        // 标准化测试文件路径（用于精确匹配）
        const normalizedTestPath = relativeTestPath.toLowerCase();
        const normalizedTestFileName = testFileName.toLowerCase();
        
        // 检查文件是否已经在任何测试目标中存在
        for (const target of targets) {
            const sourcesSection = this.extractSourcesSection(lines, target);
            
            // 从 sources 中提取所有文件路径（考虑各种 GN 路径格式）
            const sourcePaths = this.extractSourcePathsFromSection(sourcesSection, buildGnDir);
            
            // 检查是否匹配（优先完整路径匹配，避免同名文件误判）
            for (const sourcePath of sourcePaths) {
                // 标准化路径进行比较
                const normalizedSourcePath = sourcePath.toLowerCase();
                
                // 1. 优先匹配完整相对路径（精确匹配）
                if (normalizedSourcePath === normalizedTestPath) {
                    Logger.info(`[GNFileManager] ⚠️ 测试文件 "${relativeTestPath}" 已在目标 "${target.name}" 的 sources 中存在（完整路径匹配）`);
                    return {
                        success: false,
                        oldContent: content,
                        newContent: content,
                        previewNewContent: content,
                        diff: '',
                        message: `测试文件已存在于BUILD.gn的目标 "${target.name}" 中: ${relativeTestPath}（无需重复添加）`
                    };
                }
                
                // 2. 如果路径以测试文件名结尾，需要进一步验证路径关系
                // 避免不同目录下的同名文件误判
                if (normalizedSourcePath.endsWith('/' + normalizedTestFileName) || 
                    normalizedSourcePath === normalizedTestFileName) {
                    
                    // 情况A：测试文件在同目录（只有文件名，无路径）
                    if (relativeTestPath === testFileName) {
                        // 如果 GN 中的路径也是只有文件名，则匹配
                        if (sourcePath === testFileName || sourcePath.endsWith('/' + testFileName)) {
                            Logger.info(`[GNFileManager] ⚠️ 测试文件 "${testFileName}" 已在目标 "${target.name}" 的 sources 中存在（同目录匹配）`);
                            return {
                                success: false,
                                oldContent: content,
                                newContent: content,
                                previewNewContent: content,
                                diff: '',
                                message: `测试文件已存在于BUILD.gn的目标 "${target.name}" 中: ${sourcePath}（无需重复添加）`
                            };
                        }
                    } else {
                        // 情况B：测试文件在不同目录（有路径）
                        // 需要路径也匹配，不能仅匹配文件名
                        const sourceDir = normalizedSourcePath.includes('/') 
                            ? normalizedSourcePath.substring(0, normalizedSourcePath.lastIndexOf('/'))
                            : '';
                        const testDir = normalizedTestPath.includes('/')
                            ? normalizedTestPath.substring(0, normalizedTestPath.lastIndexOf('/'))
                            : '';
                        
                        // 路径必须匹配（考虑变量路径的情况，如果包含变量则宽松匹配）
                        if (sourcePath.includes('${') || sourcePath.includes('$')) {
                            // 变量路径，如果文件名匹配且路径部分相似，认为匹配
                            Logger.info(`[GNFileManager] ⚠️ 测试文件 "${relativeTestPath}" 可能在目标 "${target.name}" 的 sources 中存在（变量路径匹配）`);
                            return {
                                success: false,
                                oldContent: content,
                                newContent: content,
                                previewNewContent: content,
                                diff: '',
                                message: `测试文件可能已存在于BUILD.gn的目标 "${target.name}" 中: ${sourcePath}（变量路径，建议手动检查）`
                            };
                        } else if (sourceDir === testDir) {
                            // 路径匹配
                            Logger.info(`[GNFileManager] ⚠️ 测试文件 "${relativeTestPath}" 已在目标 "${target.name}" 的 sources 中存在（路径匹配）`);
                            return {
                                success: false,
                                oldContent: content,
                                newContent: content,
                                previewNewContent: content,
                                diff: '',
                                message: `测试文件已存在于BUILD.gn的目标 "${target.name}" 中: ${sourcePath}（无需重复添加）`
                            };
                        }
                        // 路径不匹配，继续检查其他路径
                    }
                }
            }
        }
        
        // 如果所有目标都不包含该文件，使用第一个目标来添加
        const target = targets[0];
        
        // 找到sources数组的结束位置
        let insertLine = -1;
        let indent = '';
        
        if (target.sourcesLine >= 0) {
            // 找到sources数组的闭合括号
            for (let i = target.sourcesLine; i < target.endLine; i++) {
                const line = lines[i];
                if (line.includes(']')) {
                    insertLine = i;
                    // 提取缩进
                    const match = lines[i - 1].match(/^(\s*)/);
                    indent = match ? match[1] : '    ';
                    break;
                }
            }
        }
        
        if (insertLine < 0) {
            return {
                success: false,
                oldContent: content,
                newContent: content,
                previewNewContent: content,
                diff: '',
                message: '无法找到sources数组的插入位置'
            };
        }
        
        // 构建新行
        const newLine = `${indent}"${relativeTestPath}",`;
        
        // 插入新行
        const newLines = [
            ...lines.slice(0, insertLine),
            newLine,
            ...lines.slice(insertLine)
        ];
        
        const fullNewContent = newLines.join('\n');
        const diff = this.generateDiff(content, fullNewContent, insertLine, newLine);
        
        return {
            success: true,
            oldContent: this.getContextLines(lines, insertLine - 2, insertLine + 2),           // 预览用：修改前上下文
            newContent: fullNewContent,                                                          // 完整内容：用于写入文件
            previewNewContent: this.getContextLines(newLines, insertLine - 2, insertLine + 3), // 预览用：修改后上下文
            diff,
            message: `成功添加测试文件到目标 "${target.name}"`
        };
    }
    
    /**
     * 应用修改到BUILD.gn文件
     * @param buildGnPath BUILD.gn文件路径
     * @param newContent 新内容
     * @returns 是否成功
     */
    public static applyChanges(buildGnPath: string, newContent: string): boolean {
        try {
            // 直接写入新内容（git管理版本，不需要备份）
            fs.writeFileSync(buildGnPath, newContent, 'utf-8');
            
            Logger.info(`✅ 成功更新 BUILD.gn: ${buildGnPath} (可用git diff查看更改)`);
            return true;
        } catch (error) {
            Logger.error(`Failed to update BUILD.gn: ${error}`);
            return false;
        }
    }
    
    /**
     * 提取sources部分的内容
     */
    private static extractSourcesSection(lines: string[], target: any): string {
        if (target.sourcesLine < 0) {
            return '';
        }
        
        let section = '';
        for (let i = target.sourcesLine; i < target.endLine; i++) {
            section += lines[i];
            if (lines[i].includes(']')) {
                break;
            }
        }
        return section;
    }
    
    /**
     * 从 sources 部分提取所有文件路径（考虑各种 GN 路径格式）
     * @param sourcesSection sources 部分的字符串内容
     * @param buildGnDir BUILD.gn 文件所在目录
     * @returns 标准化后的文件路径数组（相对于 buildGnDir）
     */
    private static extractSourcePathsFromSection(sourcesSection: string, buildGnDir: string): string[] {
        const paths: string[] = [];
        
        // 匹配 GN 字符串：`"path/to/file.cpp"` 或 `'path/to/file.cpp'`
        // 支持变量：`"${var}/file.cpp"` 或 `"//absolute/path/file.cpp"`
        const stringPattern = /["']([^"']+)["']/g;
        let match;
        
        while ((match = stringPattern.exec(sourcesSection)) !== null) {
            let filePath = match[1].trim();
            
            // 跳过空字符串
            if (!filePath) {
                continue;
            }
            
            // 处理 GN 绝对路径格式：`//foundation/.../file.cpp`
            // 转换为相对于项目根目录的路径（简化处理：保留原格式用于匹配）
            if (filePath.startsWith('//')) {
                // GN 绝对路径，保留格式用于匹配
                paths.push(filePath);
                continue;
            }
            
            // 处理变量路径：`${var}/file.cpp`
            // 如果包含变量，保留原格式用于匹配（无法解析变量值）
            if (filePath.includes('${') || filePath.includes('$')) {
                paths.push(filePath);
                continue;
            }
            
            // 处理相对路径：转换为相对于 buildGnDir 的标准化路径
            try {
                // 如果路径已经是绝对路径，转换为相对路径
                if (path.isAbsolute(filePath)) {
                    filePath = path.relative(buildGnDir, filePath).replace(/\\/g, '/');
                } else {
                    // 相对路径，标准化为相对于 buildGnDir 的路径
                    // 注意：如果路径不包含 `/`，说明是同目录文件，保持原样
                    if (filePath.includes('/') || filePath.startsWith('../')) {
                        const resolvedPath = path.resolve(buildGnDir, filePath);
                        filePath = path.relative(buildGnDir, resolvedPath).replace(/\\/g, '/');
                    }
                    // 否则保持原样（同目录文件，如 "file.cpp"）
                }
                
                // 标准化：移除前导的 `../` 或 `./`（但保留文件名）
                if (filePath !== path.basename(filePath)) {
                    filePath = filePath.replace(/^\.\.\//, '').replace(/^\.\//, '');
                }
                
                paths.push(filePath);
            } catch (e) {
                // 路径解析失败，保留原格式
                Logger.warn(`[GNFileManager] 无法解析路径: ${filePath}, 保留原格式`);
                paths.push(filePath);
            }
        }
        
        return paths;
    }
    
    /**
     * 获取上下文行
     */
    private static getContextLines(lines: string[], start: number, end: number): string {
        const safeStart = Math.max(0, start);
        const safeEnd = Math.min(lines.length, end);
        return lines.slice(safeStart, safeEnd).join('\n');
    }
    
    /**
     * 生成diff信息
     */
    private static generateDiff(
        oldContent: string,
        newContent: string,
        insertLine: number,
        newLine: string
    ): string {
        const oldLines = oldContent.split('\n');
        const newLines = newContent.split('\n');
        
        const contextBefore = 3;
        const contextAfter = 3;
        
        let diff = `@@ -${insertLine - contextBefore + 1},${contextBefore + contextAfter} +${insertLine - contextBefore + 1},${contextBefore + contextAfter + 1} @@\n`;
        
        // 上下文前
        for (let i = Math.max(0, insertLine - contextBefore); i < insertLine; i++) {
            diff += ` ${oldLines[i]}\n`;
        }
        
        // 新增行
        diff += `+${newLine}\n`;
        
        // 上下文后
        for (let i = insertLine; i < Math.min(oldLines.length, insertLine + contextAfter); i++) {
            diff += ` ${oldLines[i]}\n`;
        }
        
        return diff;
    }

    /**
     * 从源文件的BUILD.gn中提取配置信息
     * @param gnPath BUILD.gn文件路径
     * @param sourceFilePath 源文件路径（用于定位目标块）
     * @returns 提取的配置信息
     */
    public static extractSourceGNConfig(gnPath: string, sourceFilePath: string): {
        deps: string[];
        include_dirs: string[];
        external_deps: string[];
        cflags_cc: string[];
        module_out_path: string;
        part_name: string;
        subsystem_name: string;
        configs: string[];
        public_configs: string[];
        defines: string[];
    } | null {
        if (!fs.existsSync(gnPath)) {
            Logger.warn(`[GNFileManager] GN文件不存在: ${gnPath}`);
            return null;
        }

        try {
            const content = fs.readFileSync(gnPath, 'utf-8');
            const lines = content.split('\n');
            const sourceFileName = path.basename(sourceFilePath);

            Logger.info(`[GNFileManager] 🔍 提取 GN 配置，源文件: ${sourceFileName}`);
            Logger.info(`[GNFileManager] 📄 GN 文件: ${gnPath}`);

            // 查找包含源文件的目标块
            let targetStartLine = -1;
            let targetEndLine = -1;
            let braceDepth = 0;
            let inTarget = false;

            // 第一步：找到包含源文件的目标块
            for (let i = 0; i < lines.length; i++) {
                const line = lines[i];

                // 匹配目标定义行（各种类型）
                const targetPattern = /^\s*(ohos_shared_library|ohos_static_library|ohos_executable|ohos_source_set|shared_library|static_library|executable|source_set)\s*\(/;
                if (targetPattern.test(line) && !inTarget) {
                    targetStartLine = i;
                    inTarget = true;
                    braceDepth = 0;
                    continue;
                }

                if (inTarget) {
                    braceDepth += (line.match(/\{/g) || []).length;
                    braceDepth -= (line.match(/\}/g) || []).length;

                    // 检查是否包含源文件
                    if (line.includes(sourceFileName)) {
                        Logger.info(`[GNFileManager] ✅ 在第 ${i} 行找到源文件引用`);
                    }

                    // 目标块结束
                    if (braceDepth === 0 && targetStartLine >= 0) {
                        targetEndLine = i;
                        
                        // 检查这个块是否包含源文件
                        const targetBlock = lines.slice(targetStartLine, targetEndLine + 1).join('\n');
                        if (targetBlock.includes(sourceFileName)) {
                            Logger.info(`[GNFileManager] 🎯 找到目标块: 行 ${targetStartLine}-${targetEndLine}`);
                            break;
                        }

                        // 重置，继续查找下一个目标
                        inTarget = false;
                        targetStartLine = -1;
                    }
                }
            }

            if (targetStartLine < 0 || targetEndLine < 0) {
                Logger.warn(`[GNFileManager] ⚠️ 未找到包含源文件的目标块`);
                return null;
            }

            // 第二步：提取目标块中的配置
            const targetContent = lines.slice(targetStartLine, targetEndLine + 1).join('\n');
            const config = {
                deps: this.extractArrayField(targetContent, 'deps'),
                include_dirs: this.extractArrayField(targetContent, 'include_dirs'),
                external_deps: this.extractArrayField(targetContent, 'external_deps'),
                cflags_cc: this.extractArrayField(targetContent, 'cflags_cc'),
                configs: this.extractArrayField(targetContent, 'configs'),
                public_configs: this.extractArrayField(targetContent, 'public_configs'),
                defines: this.extractArrayField(targetContent, 'defines'),
                module_out_path: this.extractStringField(targetContent, 'module_out_path'),
                part_name: this.extractStringField(targetContent, 'part_name'),
                subsystem_name: this.extractStringField(targetContent, 'subsystem_name')
            };

            Logger.info(`[GNFileManager] 📦 提取的配置:`);
            Logger.info(`[GNFileManager]    deps: ${config.deps.length} 项`);
            Logger.info(`[GNFileManager]    include_dirs: ${config.include_dirs.length} 项`);
            Logger.info(`[GNFileManager]    external_deps: ${config.external_deps.length} 项`);
            Logger.info(`[GNFileManager]    module_out_path: ${config.module_out_path}`);

            return config;

        } catch (error) {
            Logger.error(`[GNFileManager] ❌ 提取配置失败: ${error}`);
            return null;
        }
    }

    /**
     * 从GN内容中提取数组字段
     */
    private static extractArrayField(content: string, fieldName: string): string[] {
        const result: string[] = [];
        
        // 匹配 fieldName = [ ... ] 或 fieldName += [ ... ]
        const pattern = new RegExp(`${fieldName}\\s*[+]?=\\s*\\[([^\\]]*(?:\\][^\\]]*\\[)*[^\\]]*)\\]`, 'gs');
        let match;

        while ((match = pattern.exec(content)) !== null) {
            const arrayContent = match[1];
            
            // 提取引号内的内容
            const itemPattern = /["']([^"']+)["']/g;
            let itemMatch;
            
            while ((itemMatch = itemPattern.exec(arrayContent)) !== null) {
                const item = itemMatch[1].trim();
                if (item && !result.includes(item)) {
                    result.push(item);
                }
            }
        }

        return result;
    }

    /**
     * 从GN内容中提取字符串字段
     */
    private static extractStringField(content: string, fieldName: string): string {
        // 匹配 fieldName = "value" 或 fieldName = 'value'
        const pattern = new RegExp(`${fieldName}\\s*=\\s*["']([^"']+)["']`);
        const match = content.match(pattern);
        
        return match ? match[1] : '';
    }

    /**
     * 生成完整的测试BUILD.gn内容
     * @param testTargetName 测试目标名称
     * @param testFileName 测试文件名
     * @param sourceGNConfig 从源文件GN中提取的配置（可选）
     * @param testDir 测试目录
     * @param sourceDir 源文件目录
     * @returns 生成的GN文件内容
     */
    public static generateTestBuildGN(
        testTargetName: string,
        testFileName: string,
        sourceGNConfig: ReturnType<typeof GNFileManager.extractSourceGNConfig> | null,
        testDir: string,
        sourceDir: string,
        sourceGNPath?: string,  // 新增：源 BUILD.gn 的路径，用于路径转换
        sourceFileAbsPath?: string // 新增：被测实现源码的绝对路径（用于并入 sources）
    ): string {
        const year = new Date().getFullYear();
        
        // 计算相对路径
        const relativeSourceDir = path.relative(testDir, sourceDir).replace(/\\/g, '/');
        const sourceGNDir = sourceGNPath ? path.dirname(sourceGNPath) : sourceDir;
        
        let gnContent = `# Copyright (c) ${year} Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build/test.gni")

ohos_unittest("${testTargetName}") {
`;

        // 添加 module_out_path
        if (sourceGNConfig?.module_out_path) {
            gnContent += `  module_out_path = "${sourceGNConfig.module_out_path}"\n`;
        } else {
            gnContent += `  module_out_path = "subsystem_name/part_name"\n`;
        }

        gnContent += `\n`;

        // 添加 sources（将被测实现源码一并编译，最小化单元）
        gnContent += `  sources = [\n`;
        if (sourceFileAbsPath) {
            const relImpl = path.relative(testDir, sourceFileAbsPath).replace(/\\/g, '/');
            gnContent += `    "${relImpl}",\n`;
        }
        gnContent += `    "${testFileName}",\n`;
        gnContent += `  ]\n\n`;

        // 添加 include_dirs
        gnContent += `  include_dirs = [\n`;
        if (sourceGNConfig?.include_dirs && sourceGNConfig.include_dirs.length > 0) {
            for (const dir of sourceGNConfig.include_dirs) {
                let resolvedDir = dir;
                
                // 如果是相对路径，需要转换为相对于测试目录的路径
                if (!dir.startsWith('//') && !dir.startsWith('$') && !path.isAbsolute(dir)) {
                    try {
                        // 1. 解析为绝对路径（基于源GN目录）
                        const absDir = path.resolve(sourceGNDir, dir);
                        // 2. 转换为相对于测试目录的路径
                        resolvedDir = path.relative(testDir, absDir).replace(/\\/g, '/');
                        Logger.info(`[GNFileManager] 📁 路径转换: "${dir}" → "${resolvedDir}"`);
                    } catch (e) {
                        Logger.warn(`[GNFileManager] ⚠️ 路径转换失败: ${dir}, 保留原路径`);
                    }
                }
                gnContent += `    "${resolvedDir}",\n`;
            }
        }
        // 添加源文件目录
        gnContent += `    "${relativeSourceDir}",\n`;
        gnContent += `  ]\n\n`;

        // 添加 deps
        gnContent += `  deps = [\n`;
        gnContent += `    "//third_party/googletest:gtest_main",\n`;
        if (sourceGNConfig?.deps && sourceGNConfig.deps.length > 0) {
            for (const dep of sourceGNConfig.deps) {
                // 过滤规则：
                // 1. gtest/gmock
                if (dep.includes('gtest') || dep.includes('gmock')) {
                    continue;
                }
                // 2. 本地目标（以 : 开头）
                if (dep.startsWith(':')) {
                    Logger.info(`[GNFileManager] ⚠️ 跳过本地目标: ${dep}`);
                    continue;
                }
                // 3. 相对路径目标（不以 // 或 $ 开头，且包含 /）
                if (!dep.startsWith('//') && !dep.startsWith('$') && dep.includes('/')) {
                    Logger.info(`[GNFileManager] ⚠️ 跳过相对路径目标: ${dep}`);
                    continue;
                }
                
                gnContent += `    "${dep}",\n`;
            }
        }
        gnContent += `  ]\n\n`;

        // 添加 external_deps（最小必需 + 源配置去重合并）
        const minimalExternal = [
            'googletest:gtest',
            'hilog:libhilog',
            'c_utils:utils',
            'cJSON:cjson'
        ];
        const mergedExternal = new Set<string>(minimalExternal);
        if (sourceGNConfig?.external_deps && sourceGNConfig.external_deps.length > 0) {
            for (const dep of sourceGNConfig.external_deps) {
                mergedExternal.add(dep);
            }
        }
        if (mergedExternal.size > 0) {
            gnContent += `  external_deps = [\n`;
            for (const dep of Array.from(mergedExternal)) {
                gnContent += `    "${dep}",\n`;
            }
            gnContent += `  ]\n\n`;
        }

        // 添加 configs
        if (sourceGNConfig?.configs && sourceGNConfig.configs.length > 0) {
            gnContent += `  configs = [\n`;
            for (const config of sourceGNConfig.configs) {
                gnContent += `    "${config}",\n`;
            }
            gnContent += `  ]\n\n`;
        }

        // 添加 cflags_cc（保留源配置）
        if (sourceGNConfig?.cflags_cc && sourceGNConfig.cflags_cc.length > 0) {
            gnContent += `  cflags_cc = [\n`;
            for (const flag of sourceGNConfig.cflags_cc) {
                gnContent += `    "${flag}",\n`;
            }
            gnContent += `  ]\n\n`;
        }

        // 添加 defines
        if (sourceGNConfig?.defines && sourceGNConfig.defines.length > 0) {
            gnContent += `  defines = [\n`;
            for (const define of sourceGNConfig.defines) {
                gnContent += `    "${define}",\n`;
            }
            gnContent += `  ]\n\n`;
        }

        // 添加 part_name
        if (sourceGNConfig?.part_name) {
            gnContent += `  part_name = "${sourceGNConfig.part_name}"\n`;
        }

        // 添加 subsystem_name
        if (sourceGNConfig?.subsystem_name) {
            gnContent += `  subsystem_name = "${sourceGNConfig.subsystem_name}"\n`;
        }

        // 在目标末尾追加强制 RTTI 配置（避免被后置 -fno-rtti 覆盖）
        gnContent += `  configs += [ ":force_rtti_last" ]\n`;
        gnContent += `}\n`;

        // 追加 group("unittest") 以便父级聚合引用
        gnContent += `\n` +
            `group("unittest") {\n` +
            `  testonly = true\n` +
            `  deps = [ ":${testTargetName}" ]\n` +
            `}\n`;

        // 追加本地 config，确保 -frtti 最后出现
        gnContent += `\nconfig("force_rtti_last") {\n` +
            `  cflags_cc = [ "-frtti" ]\n` +
            `}\n`;

        return gnContent;
    }
    
    /**
     * 生成子模块的 BUILD.gn（用于分层测试目录结构）
     * 包含 config、ohos_unittest 和 group 三部分
     * 
     * @param testModuleName 测试模块名（例如：managersource）
     * @param testTargetName 测试目标名（例如：DaudioSourceManagerTest）
     * @param testFileName 测试文件名（例如：daudio_source_manager_test.cpp）
     * @param sourceGNConfig 源文件的 GN 配置
     * @param testSubDir 测试子模块目录（例如：.../unittest/managersource）
     * @param sourceDir 源文件目录
     * @param projectInfo 项目信息（用于生成 module_out_path）
     * @returns 生成的 BUILD.gn 内容
     */
    public static generateSubmoduleBuildGN(
        testModuleName: string,
        testTargetName: string,
        testFileName: string,
        sourceGNConfig: ReturnType<typeof GNFileManager.extractSourceGNConfig> | null,
        testSubDir: string,
        sourceDir: string,
        projectInfo?: {
            subsystemName?: string;
            partName?: string;
            importPath?: string; // 例如：../../../../../distributedaudio.gni
        }
    ): string {
        const year = new Date().getFullYear();
        
        let gnContent = `# Copyright (c) ${year} Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build/test.gni")
`;

        // 如果有自定义的 .gni 文件需要导入
        if (projectInfo?.importPath) {
            gnContent += `import("${projectInfo.importPath}")\n`;
        }
        
        gnContent += `\n`;
        
        // 构建 module_out_path（保持与源 GN 一致，不追加 _test 后缀）
        let moduleOutPath = 'subsystem_name/part_name';
        if (projectInfo?.subsystemName && projectInfo?.partName) {
            moduleOutPath = `${projectInfo.subsystemName}/${projectInfo.partName}`;
        } else if (sourceGNConfig?.module_out_path) {
            moduleOutPath = sourceGNConfig.module_out_path;
        }
        
        gnContent += `module_out_path = "${moduleOutPath}"\n\n`;
        
        // ===== 1. config 部分 =====
        gnContent += `config("module_private_config") {
  visibility = [ ":*" ]

  include_dirs = [
    "include",
`;
        
        // 添加源文件的 include_dirs
        if (sourceGNConfig?.include_dirs && sourceGNConfig.include_dirs.length > 0) {
            for (const dir of sourceGNConfig.include_dirs) {
                gnContent += `    "${dir}",\n`;
            }
        }
        
        gnContent += `  ]
}\n\n`;
        
        // ===== 2. ohos_unittest 部分 =====
        gnContent += `## UnitTest ${testTargetName}\n`;
        gnContent += `ohos_unittest("${testTargetName}") {
  module_out_path = module_out_path

  sources = [
    "src/${testFileName}",
  ]

  configs = [ ":module_private_config" ]

`;
        
        // 添加 deps
        gnContent += `  deps = [\n`;
        if (sourceGNConfig?.deps && sourceGNConfig.deps.length > 0) {
            for (const dep of sourceGNConfig.deps) {
                // 过滤规则：跳过 gtest/gmock、本地目标、相对路径目标
                if (dep.includes('gtest') || dep.includes('gmock') || 
                    dep.startsWith(':') || 
                    (!dep.startsWith('//') && !dep.startsWith('$') && dep.includes('/'))) {
                    continue;
                }
                gnContent += `    "${dep}",\n`;
            }
        }
        gnContent += `  ]\n\n`;
        
        // 添加 external_deps
        gnContent += `  external_deps = [\n`;
        gnContent += `    "googletest:gmock",\n`;
        gnContent += `    "googletest:gtest",\n`;
        gnContent += `    "hilog:libhilog",\n`;
        
        if (sourceGNConfig?.external_deps && sourceGNConfig.external_deps.length > 0) {
            for (const dep of sourceGNConfig.external_deps) {
                // 跳过重复的 gtest/gmock
                if (dep.includes('gtest') || dep.includes('gmock')) {
                    continue;
                }
                gnContent += `    "${dep}",\n`;
            }
        }
        
        gnContent += `  ]
}\n\n`;
        
        // ===== 3. group 部分 =====
        gnContent += `group("${testModuleName}_test") {
  testonly = true
  deps = [ ":${testTargetName}" ]
}\n`;
        
        return gnContent;
    }
    
    /**
     * 在父级 group 的 deps 中注册新的子模块
     * 
     * @param parentGroupGnPath 父级 group BUILD.gn 的路径
     * @param testModuleName 测试模块名
     * @param depPath 依赖路径（可选，如果不提供会自动推断）
     * @returns 是否成功
     */
    public static registerSubmoduleToParentGroup(
        parentGroupGnPath: string,
        testModuleName: string,
        depPath?: string
    ): { success: boolean; message: string } {
        try {
            if (!fs.existsSync(parentGroupGnPath)) {
                return { success: false, message: `父级 BUILD.gn 不存在: ${parentGroupGnPath}` };
            }
            
            let content = fs.readFileSync(parentGroupGnPath, 'utf-8');
            
            // 检查是否已经存在
            if (content.includes(`/${testModuleName}:`)) {
                Logger.info(`[GNFileManager] 子模块 ${testModuleName} 已在父级 group 中注册`);
                return { success: true, message: '子模块已存在' };
            }
            
            // 查找 deps 数组
            const depsRegex = /deps\s*=\s*\[\s*([^\]]*)\]/s;
            const match = content.match(depsRegex);
            
            if (!match) {
                return { success: false, message: '未找到 deps 数组' };
            }
            
            // 如果没有提供 depPath，从现有 deps 中推断路径模式
            if (!depPath) {
                depPath = this.inferDepPath(match[1], parentGroupGnPath, testModuleName);
                Logger.info(`[GNFileManager] 推断的 dep 路径: ${depPath}`);
            }
            
            // 构建新的 dep 行
            const newDepLine = `    "${depPath}",`;
            
            // 在最后一个 dep 后添加
            const depsContent = match[1];
            const lines = depsContent.split('\n');
            
            // 找到最后一个非空行
            let lastNonEmptyIndex = -1;
            for (let i = lines.length - 1; i >= 0; i--) {
                if (lines[i].trim().length > 0) {
                    lastNonEmptyIndex = i;
                    break;
                }
            }
            
            if (lastNonEmptyIndex >= 0) {
                lines.splice(lastNonEmptyIndex + 1, 0, newDepLine);
            } else {
                lines.push(newDepLine);
            }
            
            // 🔧 修复：确保每一行都有正确的缩进（4个空格）
            const normalizedLines = lines.map(line => {
                const trimmed = line.trim();
                if (trimmed.length === 0) {
                    return '';  // 空行保持为空
                }
                // 如果行已经有缩进，保留；否则添加4个空格的缩进
                if (line.startsWith('    ') || line.startsWith('\t')) {
                    return line;  // 已有缩进
                }
                return `    ${trimmed}`;  // 添加4个空格
            });
            
            const newDepsContent = normalizedLines.join('\n');
            const newContent = content.replace(depsRegex, `deps = [\n${newDepsContent}\n  ]`);
            
            fs.writeFileSync(parentGroupGnPath, newContent, 'utf-8');
            Logger.info(`[GNFileManager] ✅ 成功在父级 group 中注册子模块: ${testModuleName}`);
            
            return { success: true, message: '成功注册子模块' };
        } catch (error) {
            Logger.error(`[GNFileManager] 注册子模块失败: ${error}`);
            return { success: false, message: `注册失败: ${error}` };
        }
    }
    
    /**
     * 从现有 deps 中推断新的 dep 路径
     * 
     * @param depsContent deps 数组的内容
     * @param parentGroupGnPath 父级 GN 文件路径
     * @param testModuleName 测试模块名
     * @returns 推断的 dep 路径
     */
    private static inferDepPath(depsContent: string, parentGroupGnPath: string, testModuleName: string): string {
        // 提取所有现有的 dep 行
        const depLines = depsContent.split('\n')
            .map(line => line.trim())
            .filter(line => line.startsWith('"') && line.includes(':'));
        
        Logger.info(`[GNFileManager] 分析现有 deps，共 ${depLines.length} 个依赖`);
        
        if (depLines.length === 0) {
            // 没有现有 deps，使用相对路径
            Logger.warn(`[GNFileManager] 没有找到现有 deps，使用相对路径`);
            return `./${testModuleName}:${testModuleName}_test`;
        }
        
        // 查找包含 unittest 的 dep（最可能的模式）
        const unittestDeps = depLines.filter(line => line.includes('/unittest/'));
        
        if (unittestDeps.length > 0) {
            // 分析第一个 unittest dep 的模式
            const exampleDep = unittestDeps[0].replace(/^"|",?$/g, ''); // 去掉引号
            Logger.info(`[GNFileManager] 参考示例: ${exampleDep}`);
            
            // 提取路径前缀（直到 /unittest/）
            const unittestIndex = exampleDep.indexOf('/unittest/');
            if (unittestIndex > 0) {
                const pathPrefix = exampleDep.substring(0, unittestIndex + '/unittest/'.length);
                const newDepPath = `${pathPrefix}${testModuleName}:${testModuleName}_test`;
                
                Logger.info(`[GNFileManager] 路径前缀: ${pathPrefix}`);
                Logger.info(`[GNFileManager] 新路径: ${newDepPath}`);
                
                return newDepPath;
            }
        }
        
        // 如果没有找到 unittest 模式，尝试查找同级目录的模式
        for (const depLine of depLines) {
            const dep = depLine.replace(/^"|",?$/g, '');
            
            // 查找最后一个目录分隔符
            const lastSlashIndex = dep.lastIndexOf('/');
            const lastColonIndex = dep.lastIndexOf(':');
            
            if (lastSlashIndex > 0 && lastColonIndex > lastSlashIndex) {
                // 提取目录名
                const dirName = dep.substring(lastSlashIndex + 1, lastColonIndex);
                
                // 检查是否是子模块目录（不是以 _test 结尾的路径）
                if (dirName && !dirName.endsWith('_test')) {
                    // 用新的模块名替换
                    const pathPrefix = dep.substring(0, lastSlashIndex + 1);
                    const newDepPath = `${pathPrefix}${testModuleName}:${testModuleName}_test`;
                    
                    Logger.info(`[GNFileManager] 从 ${dep} 推断路径前缀: ${pathPrefix}`);
                    Logger.info(`[GNFileManager] 新路径: ${newDepPath}`);
                    
                    return newDepPath;
                }
            }
        }
        
        // 最后的fallback：使用相对路径
        Logger.warn(`[GNFileManager] 无法推断路径模式，使用相对路径`);
        return `./${testModuleName}:${testModuleName}_test`;
    }
}

